package com.daona.security.service;

import com.daona.security.auth.AuthenticationRequest;
import com.daona.security.auth.AuthenticationResponse;
import com.daona.security.auth.RegisterRequest;
import com.daona.security.token.Token;
import com.daona.security.token.TokenRepository;
import com.daona.security.token.TokenType;
import com.daona.security.user.User;
import com.daona.security.user.UserRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.io.IOException;

@Service
@RequiredArgsConstructor
public class AuthenticationService {
  //访问user数据库
  private final UserRepository repository;

  //访问token数据库
  private final TokenRepository tokenRepository;

  //密码加密器
  private final PasswordEncoder passwordEncoder;

  //JWT 相关方法
  private final JwtService jwtService;

  //Spring Security 认证管理器
  private final AuthenticationManager authenticationManager;

  /**
   * 注册方法
   * @param request 请求体
   * @return AuthenticationResponse(自己封装的响应结构)
   */
  public AuthenticationResponse register(RegisterRequest request) {

    //构建用户信息
    var user = User.builder()
        .firstname(request.getFirstname())
        .lastname(request.getLastname())
        .email(request.getEmail())
        .password(passwordEncoder.encode(request.getPassword()))
        .role(request.getRole())
        .build();

    //将用户信息保存到数据库
    var savedUser = repository.save(user);

    //通过JWT方法生成Token
    var jwtToken = jwtService.generateToken(user);

    //生成RefreshToken(刷新Token使用)
    var refreshToken = jwtService.generateRefreshToken(user);

    //将Token保存到数据库
    saveUserToken(savedUser, jwtToken);

    //返回响应体
    return AuthenticationResponse.builder().accessToken(jwtToken).refreshToken(refreshToken).build();
  }

  /**
   * 鉴权(登录)方法
   * @param request 请求体
   * @return AuthenticationResponse(自己封装的响应结构)
   */
  public AuthenticationResponse authenticate(AuthenticationRequest request) {

    //通过Spring Security 认证管理器进行认证
    authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(
            request.getEmail(),
            request.getPassword()
        )
    );
    //通过邮箱查询用户信息,当前项目email就是账号
    var user = repository.findByEmail(request.getEmail()).orElseThrow();

    //通过JWT方法生成Token
    var jwtToken = jwtService.generateToken(user);

    //生成RefreshToken(刷新Token使用)
    var refreshToken = jwtService.generateRefreshToken(user);

    //将之前所有的Token变成失效状态
    revokeAllUserTokens(user);

    //保存新的Token到数据库
    saveUserToken(user, jwtToken);

    //封装响应体
    return AuthenticationResponse.builder().accessToken(jwtToken).refreshToken(refreshToken).build();
  }

  /**
   * 保存用户Token方法
   * 构建Token实体后保存到数据库
   * @param user 用户信息
   * @param jwtToken Token
   */
  private void saveUserToken(User user, String jwtToken) {
    var token = Token.builder()
        .user(user)
        .token(jwtToken)
        .tokenType(TokenType.BEARER)
        .expired(false)
        .revoked(false)
        .build();
    tokenRepository.save(token);
  }

  /**
   * 将用户所有Token变成失效状态
   * @param user 用户信息
   */
  private void revokeAllUserTokens(User user) {
    var validUserTokens = tokenRepository.findAllValidTokenByUser(user.getId());
    if (validUserTokens.isEmpty())
      return;
    validUserTokens.forEach(token -> {
      token.setExpired(true);
      token.setRevoked(true);
    });
    tokenRepository.saveAll(validUserTokens);
  }

  /**
   * 刷新token方法
   * @param request 请求体
   * @param response 响应体
   * @throws IOException 抛出IO异常
   */
  public void refreshToken(HttpServletRequest request, HttpServletResponse response) throws IOException {

    //从请求头中获取中获取鉴权信息 AUTHORIZATION
    final String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
    final String refreshToken;
    final String userEmail;
    //如果鉴权信息为空或者不是以Bearer 开头的,直接返回
    if (authHeader == null ||!authHeader.startsWith("Bearer ")) {
      return;
    }
    //从鉴权信息中获取RefreshToken
    refreshToken = authHeader.substring(7);

    //从RefreshToken中获取用户信息
    userEmail = jwtService.extractUsername(refreshToken);
    if (userEmail != null) {
      //根据用户信息查询用户,如果用户不存在抛出异常
      var user = this.repository.findByEmail(userEmail).orElseThrow();
      //验证Token是否有效
      if (jwtService.isTokenValid(refreshToken, user)) {
        var accessToken = jwtService.generateToken(user);
        revokeAllUserTokens(user);
        saveUserToken(user, accessToken);
        //生成新的Token和RefreshToken并通过响应体返回
        var authResponse = AuthenticationResponse.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken)
                .build();
        new ObjectMapper().writeValue(response.getOutputStream(), authResponse);
      }
    }
  }
}
